﻿//-----------------------------------------------------------------------------------------------------------
// WBFSSync Project by Omega Frost 
// http://wbfssync.codeplex.com/
//
// WBFSSync is Licensed under the terms of the 
// Microsoft Reciprocal License (Ms-RL)
//-----------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using WBFSe3.Wbfs;
using WBFSe3.Streams;
using WBFSe3.IO;
using WBFSe3.Interfaces;

namespace WBFSe3.Formats
{
    //-------------------------------------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------------------------------------
    public class WbfsFWbfsReader : WbfsFormatReader
    {
        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        int numSectors;
        int sectorSizeLsh;
        int wbfsSecsPerDisc;
        ushort[] sectors;


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private int NameOrderer(String key)
        {
            Char c = Char.ToLower(key[key.Length - 1]);
            if (c == 's') return 0;
            else return int.Parse(c.ToString());
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsError Initialize(WbfsDriveInfo drive, WbfsPath directory, string file)
        {
            String search = file.Substring(0, file.Length - 1);
            List<String> files = new List<String>();
            for (int i = 0; i < directory.Count; i++)
            {
                if (directory.Files[i].StartsWith(search, StringComparison.OrdinalIgnoreCase))
                {
                    files.Add(directory.Files[i]);
                    directory.Files[i] = null;
                }
            }

            FileStream f = File.OpenRead(System.IO.Path.Combine(directory.Path, file));
            Byte[] header = new Byte[512];
            f.Read(header, 0, 512);

            this.version = 1;
            this.sectorSizeLsh = header[WbfsConstants.WbfsHeadWbfsSecSzPos];
            this.sectorSize = 1 << header[WbfsConstants.WbfsHeadWbfsSecSzPos];
            
            this.path = new WbfsPath(directory.Path, 
                files.ToArray());

            this.path.Files = this.path.Files.OrderBy(NameOrderer).ToArray();

            this.fileSize = 0;
            for (int i = 0; i < this.path.Count; i++)
            {
                FileInfo fi = new FileInfo(this.path[i]);
                this.fileSize += fi.Length;
                fi = null;
            }

            this.numSectors = (int)(this.fileSize / 
                this.sectorSize);

            this.wbfsSecsPerDisc = WbfsHelper.
                WbfsSecsLshPerDisc(this.sectorSizeLsh);

            header = new byte[this.sectorSize - 512];
            f.Read(header, 0, this.sectorSize - 512);
            f.Close();

            this.fileLength = this.wbfsSecsPerDisc / 2;
            this.sectors = new ushort[this.wbfsSecsPerDisc];
            for (int i = 0; i < this.wbfsSecsPerDisc; i++)
            {
                if((this.sectors[i] = (ushort)WbfsHelper.ntohs(header,
                    WbfsConstants.WbfsDiscHCopySz + 2 * i)) != 0)
                {
                    if (i > this.wbfsSecsPerDisc / 2)
                        this.fileLength = this.wbfsSecsPerDisc;
                }
            }

            this.fileLength *= this.sectorSize;
            
            return WbfsError.Ok;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsError Fix()
        {
            // Sorry... no way... for now
            return WbfsError.FormatBadFile;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsError ChangeHeader(string name, string code, WbfsRegion region)
        {
            // Calculate some things...

            int wbfsSecsPerDisc = WbfsHelper.WbfsSecsLshPerDisc(
                WbfsConstants.WbfsDefaultSectorSizeLsh);

            int nbytes = WbfsHelper.FitBit(this.numSectors, 8);

            int sectorTablePos = WbfsHelper.Align(
                WbfsConstants.WbfsDefaultSectorSize - nbytes, 512);

            int sectorTableSize = WbfsHelper.Fit(nbytes, 512);

            int discDescriptorSize = WbfsHelper.Fit(WbfsConstants.WbfsDiscHCopySz +
                wbfsSecsPerDisc * sizeof(ushort), 512);

            int maxDiscs = Math.Min((sectorTablePos - 512) / discDescriptorSize, 512 - 12);

            //

            FileStream f = File.Open(this.path[0], FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
            f.Seek(512 + WbfsConstants.WbfsDiscHCopySz + WbfsConstants.WiiCodePos, SeekOrigin.Begin);
            f.Write(Encoding.Default.GetBytes(code), 0, WbfsConstants.WiiCodeLen);
            f.Write(Encoding.Default.GetBytes(name), 0, WbfsConstants.WiiNameLen);

            return WbfsError.Ok;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        protected override WbfsError EmmitStream(FileAccess access, FileShare share, out WbfsStream stream)
        {
            WbfsMultiFileStream baseStream = new WbfsMultiFileStream(this.path, null, this.sectorSize, FileMode.Open);
            WbfsSectorStream sectorStream = new WbfsSectorStream(baseStream, this.sectorSize, this.sectors);
            stream = sectorStream;

            return WbfsError.Ok;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override void Close()
        {
            // Do nothing...
        }
    }
}
